'''
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http:#www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''
from base.java.exception.IllegalStateException import IllegalStateException
from base.java.exception.UnsupportedOperationException import UnsupportedOperationException
from base.shared.JenaException import JenaException

#import java.io.IOException;
#import java.io.ObjectStreamException;
#import java.io.Serializable;
#import java.util.function.Function;

#import org.apache.jena.datatypes.RDFDatatype;
#import org.apache.jena.graph.impl.LiteralLabel;
#import org.apache.jena.shared.JenaException;
#import org.apache.jena.shared.PrefixMapping;
#import org.apache.jena.sys.Serializer;

'''
 * A Node has five subtypes: Node_Blank, Node_Anon, Node_URI,
 * Node_Variable, and Node_ANY.
 * Nodes are only constructed by the node factory methods, and they will
 * attempt to re-use existing nodes with the same label if they are recent
 * enough.
'''

from abc import ABC, abstractmethod

'''
 * The canonical instance of Node_ANY. No other instances are required.
'''
# param
# result static final Node ANY = new
#ANY = Node_ANY()

# Serializable
class Node(ABC):

    #final protected Object
    label = None
    #static final int
    THRESHOLD = 10000

    #static final String
    RDFprefix = "http:#www.w3.org/1999/02/22-rdf-syntax-ns#"

    '''
     * Visit a Node and dispatch on it to the appropriate method from the
     * NodeVisitor <code>v</code>.
     *
     * @param v the visitor to apply to the node
     * @return the value returned by the applied method
    '''
    # param NodeVisitor v
    # result abstract Object
    @abstractmethod
    def visitWith(self, v):
        pass

    '''
     * Answer true iff this node is concrete, ie not variable, ie URI, blank, or literal.
    '''
    # param 
    # result abstract boolean
    @abstractmethod
    def isConcrete(self):
        pass

    '''
     * Answer true iff this node is a literal node [subclasses override]
    '''
    # param 
    # result boolean
    def isLiteral(self):
        return False
    

    '''
     * Answer true iff this node is a blank node [subclasses override]
    '''
    # param 
    # result boolean
    def isBlank(self):
        return False
    

    '''
     * Answer true iff this node is a URI node [subclasses override]
    '''
    # param 
    # result boolean
    def isURI(self):
        return False
    

    '''
     * Answer true iff this node is a variable node - subclasses override
    '''
    # param 
    # result boolean
    def isVariable(self):
        return False
    

    '''
     * Answer true iff this node is an "triple node" (RDF-star)
    '''
    # param 
    # result boolean
    def isNodeTriple(self):
        return False
    

    '''
     * Answer true iff this node is an "graph node" (N3 formula).
     * This is not related to named graphs.
    '''
    # param 
    # result boolean
    def isNodeGraph(self):
        return False
    

    '''
     * Extension node. Typically used in data structures based on triples.
    '''
    # param 
    # result boolean
    def isExt(self):
        return False
    

    '''
     * get the blank node id if the node is blank, otherwise die horribly
    '''
    # param 
    # result BlankNodeId
    # @abstractmethod
    def getBlankNodeId(self):
        raise UnsupportedOperationException(str(self) + " is not a blank node")
    

    '''
     * Answer the label of this blank node or throw an UnsupportedOperationException
     * if it's not blank.
    '''
    # param 
    # result String
    # @abstractmethod
    def getBlankNodeLabel(self):
        return self.getBlankNodeId().getLabelString()
    

    '''
     * Answer the literal value of a literal node, or throw an UnsupportedOperationException
     * if it's not a literal node
    '''
    # param 
    # result LiteralLabel
    # @abstractmethod
    def getLiteral(self):
        raise UnsupportedOperationException(str(self) + " is not a literal node")
    

    '''
     * Answer the value of this node's literal value, if it is a literal
     * otherwise die horribly.
    '''
    # param 
    # result Object
    # @abstractmethod
    def getLiteralValue(self):
        raise self.NotLiteral(self)
    

    '''
     * Answer the lexical form of this node's literal value, if it is a literal
     * otherwise die horribly.
    '''
    # param 
    # result String
    # @abstractmethod
    def getLiteralLexicalForm(self):
        raise self.NotLiteral(self)
    

    '''
     * Answer the language of this node's literal value, if it is a literal
     * otherwise die horribly.
    '''
    # param 
    # result String
    # @abstractmethod
    def getLiteralLanguage(self):
        raise self.NotLiteral(self)
    

    '''
     * Answer the data-type URI of this node's literal value, if it is a
     * literal otherwise die horribly.
    '''
    # param 
    # result String
    # @abstractmethod
    def getLiteralDatatypeURI(self):
        raise self.NotLiteral(self)


    '''
     * Answer the RDF datatype object of this node's literal value, if it is
     * a literal otherwise die horribly.
    '''
    # param 
    # result RDFDatatype
    # @abstractmethod
    def getLiteralDatatype(self):
        raise self.NotLiteral(self)
    

    # param 
    # result boolean
    # @abstractmethod
    def getLiteralIsXML(self):
        raise self.NotLiteral(self)
    

    '''
     * Exception thrown if a literal-access operation is attempted on a
     * non-literal node.
    '''
    #public static
    class NotLiteral(JenaException):
        def __init__(self, it):
            super().__init__(str(it) + " is not a literal node")
        # public NotLiteral(Node it):
        #     super().__init__(it + " is not a literal node")
        
    

    '''
     * Answer the object which is the index value for this Node. The default
     * is this Node itself overridden in Node_Literal for literal indexing
     * purposes. Only concrete nodes should use this method.
    '''
    # param 
    # result Object
    # @abstractmethod
    def getIndexingValue(self):
        return self
    

    '''
     * get the URI of this node if it has one, else die horribly
    '''
    # param 
    # result String
    # @abstractmethod
    def getURI(self):
        raise UnsupportedOperationException(str(self) + " is not a URI node")
    

    '''
     * get the namespace part of this node if it's a URI node, else die horribly
    '''
    # param 
    # result String
    # @abstractmethod
    def getNameSpace(self):
        raise UnsupportedOperationException(str(self) + " is not a URI node")
    

    '''
     * get the localname part of this node if it's a URI node, else die horribly
    '''
    # param 
    # result String
    # @abstractmethod
    def getLocalName(self):
        raise UnsupportedOperationException(str(self) + " is not a URI node")
    

    '''
     * get a variable nodes name, otherwise die horribly
    '''
    # param 
    # result String
    # @abstractmethod
    def getName(self):
        raise UnsupportedOperationException("this (" + type(self).__name__ + ") is not a variable node")
    

    '''
     * Get the triple for a triple term (embedded triple), otherwise die horribly
    '''
    # param 
    # result Triple
    # @abstractmethod
    def getTriple(self):
        raise UnsupportedOperationException("this (" + type(self).__name__ + ") is not a embedded triple node")
    

    '''
     * Get the graph for a graph term (N3 formula), otherwise die horribly
    '''
    # param 
    # result Graph
    # @abstractmethod
    def getGraph(self):
        raise UnsupportedOperationException("this (" + type(self).__name__ + ") is not a graph-valued node")
    

    '''
     * answer true iff this node is a URI node with the given URI
    '''
    # param String uri
    # result boolean
    # @abstractmethod
    def hasURI(self, uri):
        return False


    ''' package visibility only'''
    # Node(Object label):
    #     this.label = label
    def __init__(self, label):
        self.label = label

    '''
     * Nodes only equal other Nodes that have equal labels.
    '''
    #@Override
    # param Object o
    # result abstract boolean
    # @abstractmethod
    def equals(self, o):
        #TODO 需要具体看一下
        return self == o

    '''
     * Test that two nodes are semantically equivalent.
     * In some cases this may be the same as equals, in others
     * equals is stricter. For example, two xsd:int literals with
     * the same value but different lexical form are semantically
     * equivalent but distinguished by the java equals function.
     * <p>Default implementation is to use equals, subclasses should
     * override this.</p>
    '''
    # param Object o
    # result boolean
    # @abstractmethod
    def sameValueAs(self, o):
        return self.equals(o)
    

    #@Override
    # param 
    # result int
    # @abstractmethod
    def hashCode(self):
        return hash(self.label) * 31
    

    '''
     * Answer true iff this node accepts the other one as a match.
     * The default is an equality test it is over-ridden in subclasses to
     * provide the appropriate semantics for literals, ANY, and variables.
     *
     * @param other a node to test for matching
     * @return true iff this node accepts the other as a match
    '''
    # param Node other
    # result boolean
    # @abstractmethod
    def matches(self, other):
        return self.equals(other)
    

    # ---- Serializable
    # Must be "protected", not "private".
    # param 
    # result Object
    # @abstractmethod
    #throws ObjectStreamException:
    # 方法没有使用
    def writeReplace(self):
        #Function<Node, Object>
        # function = Serializer.getNodeSerializer()
        # if (function is None):
        #     raise IllegalStateException("Function for Node.writeReplace not set")
        # return function.apply(self)
        pass
    

    # Any attempt to serialize without replacement is an error.
    # param java.io.ObjectOutputStream out
    # result void
    # @abstractmethod
    #pass throws IOException:
    def writeObject(self, out):
        raise IllegalStateException(None)
    

    # param java.io.ObjectInputStream in
    # result void
    # @abstractmethod
    # pass throws IOException, ClassNotFoundException:
    def readObject(self, input):
        raise IllegalStateException(None)
    
    # ---- Serializable

    '''
     * Answer a human-readable representation of this Node. It will not compress URIs,
     * nor quote literals (because at the moment too many places use toString() for
     * something machine-oriented).
    '''
    #@Override
    # param 
    # result String
    # @abstractmethod
    # def toString(self):
    #     return toString(null)
    

    '''
     * Answer a human-readable representation of this Node where literals are
     * quoted according to <code>quoting</code> but URIs are not compressed.
    '''
    # param boolean quoting
    # result String
    # @abstractmethod
    # def toString(self, quoting):
    #     return toString(null, quoting)
    

    '''
     * Answer a human-readable representation of the Node, quoting literals and
     * compressing URIs.
    '''
    # param PrefixMapping pm
    # result String
    # @abstractmethod
    # def toString(self, pm):
    #     return toString(pm, true)
    #

    '''
     * Answer a human readable representation of this Node, quoting literals if specified,
     * and compressing URIs using the prefix mapping supplied.
    '''
    # param PrefixMapping pm, boolean quoting
    # result String
    # @abstractmethod
    def toString(self):
        return self.label.toString()
    

